package org.inspursc.s0517.health.evaluation.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import org.inspursc.s0517.health.evaluation.dto.*;
import org.inspursc.s0517.health.evaluation.enums.AppraiseStutasEnum;
import org.inspursc.s0517.health.evaluation.enums.SuggestByScoreEnum;
import org.inspursc.s0517.health.evaluation.service.SurveyAppraiseService;
import org.inspursc.s0517.health.evaluation.utils.DataTypeUtil;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;



public class BasicSurveyAppraiseServiceImpl implements SurveyAppraiseService {

    /**
     *  assemble all analyse
     */
    @Override
    public Map<String, Object> appraise(Multimap<String, AnRadioDTO> anRadioMultimap, CustomerDTO customer, List<QuestionCategoryDTO> categoryDTOS) {

        //get category ID & get category name include first class category and second class category
        //for assemble detail analyse report
        Map<Integer, String> cateNameMap = Maps.newHashMap();
        for (QuestionCategoryDTO categoryDTO : categoryDTOS) {
            cateNameMap.put(categoryDTO.getId(), categoryDTO.getName());
        }

        //for assemble second category list
        Multimap<String, SecondCategoryKpiDTO> secondCateKpiMap = ArrayListMultimap.create();
        //for get first category id and assemble its second category analyse detail
        Multimap<Integer, SecondCategoryKpiDTO> fristIdMultiMap = ArrayListMultimap.create();
        //get second category detail analyse report
        for (String dto : anRadioMultimap.keySet()) {
            //get second category answer list
            List<AnRadioDTO> anRadios = ((ArrayListMultimap<String, AnRadioDTO>) anRadioMultimap).get(dto);
            //sum
            SecondCategoryKpiDTO secondCategoryKpiDTO = this.getSecondCategoryKpi(dto, anRadios);
            //get second category name and set it to secondCategoryKpiDTO
            String cataName = cateNameMap.get(Integer.parseInt(dto.substring(dto.lastIndexOf("-") + 1)));
            secondCategoryKpiDTO.setName(cataName);
            //get first category id for assemble
            Integer firstInt = Integer.parseInt(dto.substring(0, dto.lastIndexOf("-")));
            //assemble report
            secondCateKpiMap.put(dto, secondCategoryKpiDTO);
            fristIdMultiMap.put(firstInt, secondCategoryKpiDTO);
        }

        //first category list
        List<CategoryKpiDTO> list = Lists.newArrayList();
        //assemble first category analyse report list
        for (Integer firstInt : fristIdMultiMap.keySet()) {
            //by first second category get second category list
            List<SecondCategoryKpiDTO> secondCategoryKpiDTO = ((ArrayListMultimap<Integer, SecondCategoryKpiDTO>) fristIdMultiMap).get(firstInt);
            //set analyse status for second category
            List<String> statusStr = secondCategoryKpiDTO.parallelStream().map(SecondCategoryKpiDTO::getStatus).collect(Collectors.toList());
            for (SecondCategoryKpiDTO dto : secondCategoryKpiDTO) {
                statusStr.add(dto.getStatus());
            }
            //assemble all info of first category
            String status = this.getStatusStr(statusStr);
            CategoryKpiDTO categoryKpiDTO = this.getCategoryKpi(secondCategoryKpiDTO);
            categoryKpiDTO.setName(cateNameMap.get(firstInt));
            categoryKpiDTO.setStatus(status);
            list.add(categoryKpiDTO);
        }
        //asseble all analyse detail report
        Double total = list.stream().mapToDouble(CategoryKpiDTO::getScore).sum();
        String conclu = this.getConclusionByTotalScore(total);
        Map<String, Object> map = Maps.newHashMap();
        map.put("categoryKpi", list);
        map.put("total_score", DataTypeUtil.getInt(total));
        map.put("description", conclu);
        return map;
    }
    //return compute result to appraise function for assemble analyse report
    private SecondCategoryKpiDTO getSecondCategoryKpi(String cateId, List<AnRadioDTO> anRadioDTOS) {
        switch (cateId) {
            case "21-26": // assemble by data table named t_question_category  id & parent_id
                return this.setDailyLifeKpi(anRadioDTOS);
            case "21-27":
                return this.setRiskOfFallingKpi(anRadioDTOS);
            case "21-38":
                return this.setPainKpi(anRadioDTOS);
            case "21-29":
                return this.setSleepQualityKpi(anRadioDTOS);
            case "21-30":
                return this.setInstrumentDailylifeKpi(anRadioDTOS);
            case "21-31":
                return this.setPerceptiveAbilityKpi(anRadioDTOS);
            case "21-32":
                return this.setSocialParticipationKpi(anRadioDTOS);
            case "21-34":
                return this.setPainOfQuantitativeKpi(anRadioDTOS);
            case "21-35":
                return this.setInitiativeKpi(anRadioDTOS);
            case "21-36":
                return this.setBalanceKpi(anRadioDTOS);
            default:
                return this.setSecondCategoryKpi(anRadioDTOS);
        }
    }
    /**
     * get first category score list
     */
    private CategoryKpiDTO getCategoryKpi (List<SecondCategoryKpiDTO> secondCategoryKpiDTO){
        CategoryKpiDTO categoryKpiDTO = new CategoryKpiDTO();
        Double totalScore = secondCategoryKpiDTO.stream().mapToDouble(SecondCategoryKpiDTO::getScore).sum();
        categoryKpiDTO.setSecondCategoryKpi(secondCategoryKpiDTO);
        categoryKpiDTO.setScore(DataTypeUtil.doubleFormatTwo(totalScore));
        categoryKpiDTO.setDescription(" ");
        return categoryKpiDTO;
    }
    /**
     * get second category score
     * for default compute used by getSecondCategoryKpi function
     * if just sum use this function
     * 1.psychological status
     * 2.language ability
     */
    private SecondCategoryKpiDTO setSecondCategoryKpi(List<AnRadioDTO> anRadioDTOS) {
        //for compute second class
        SecondCategoryKpiDTO secondCategoryDTO = new SecondCategoryKpiDTO();
        //total score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();
        double finalTotalScore = totalScore/100;
        secondCategoryDTO.setScore(finalTotalScore);
        return secondCategoryDTO;
    }
    /**
     * Second category personalization function :
     * Ability to act in daily life
     */
    private SecondCategoryKpiDTO setDailyLifeKpi(List<AnRadioDTO> anRadioDTOS){
        //for compute daily life
        SecondCategoryKpiDTO secondCategoryKpiDTO = new SecondCategoryKpiDTO();
        //total score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();

        double finalTotalScore = 0;
        if (totalScore > 90){
            finalTotalScore = 10;
        }else if(totalScore > 80){
            finalTotalScore = 8;
        }else if(totalScore > 60){
            finalTotalScore = 6;
        }else if(totalScore > 40){
            finalTotalScore = 4;
        }else if(totalScore > 20){
            finalTotalScore = 2;
        }else if(totalScore > 1){
            finalTotalScore = 0;
        }else {
            //todo
        }
        secondCategoryKpiDTO.setScore(finalTotalScore);
        return secondCategoryKpiDTO;
    }
    /**
     * Second category personalization function :
     * Risk of falling
     */
    private SecondCategoryKpiDTO setRiskOfFallingKpi(List<AnRadioDTO> anRadioDTOS){
        //for compute
        SecondCategoryKpiDTO secondCategoryKpiDTO = new SecondCategoryKpiDTO();
        //total score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();
        double finalScore = 0;
        if(totalScore == 0){
            finalScore = 5;
        }else if(totalScore == 1){
            finalScore = 4;
        }else if(totalScore == 2){
            finalScore = 3;
        }else if(totalScore == 3){
            finalScore = 2;
        }else if(totalScore == 4){
            finalScore = 1;
        }else if(totalScore >= 5){
            finalScore = 0;
        }else {
            //todo
        }
        secondCategoryKpiDTO.setScore(finalScore);
        return  secondCategoryKpiDTO;
    }
    /**
     * Second category personalization function :
     * pain
     */
    private SecondCategoryKpiDTO setPainKpi(List<AnRadioDTO> anRadioDTOS){
        //for compute
        SecondCategoryKpiDTO secondCategoryKpiDTO = new SecondCategoryKpiDTO();
        //total score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();

        double finalScore = 0;
        if(totalScore == 0){
            finalScore = 3;
        }else if(totalScore >7){
            finalScore = 0;
        }else if(totalScore >3){
            finalScore = 1;
        }else if(totalScore >1){
            finalScore = 2;
        }else {
            //todo
        }
        secondCategoryKpiDTO.setScore(finalScore);
        return  secondCategoryKpiDTO;
    }
    /**
     * Second category personalization function :
     * The quality of sleep
     */
    private SecondCategoryKpiDTO setSleepQualityKpi(List<AnRadioDTO> anRadioDTOS){
        //for compute
        SecondCategoryKpiDTO secondCategoryKpiDTO = new SecondCategoryKpiDTO();
        //total Score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();
        totalScore = totalScore/100;

        double finalScore = 0;
        if(totalScore == 18){
            finalScore = 5 ;
        }else if(totalScore > 15){
            finalScore = 4;
        }else if(totalScore > 11){
            finalScore = 3;
        }else if(totalScore > 8){
            finalScore = 2;
        }else if(totalScore > 5){
            finalScore = 1;
        }else if(totalScore > 0){
            finalScore = 0;
        }else {
            //todo
        }
        secondCategoryKpiDTO.setScore(finalScore);
        return secondCategoryKpiDTO;
    }
    /**
     * Second category personalization function :
     * Instrumental ability in daily life
     */
    private SecondCategoryKpiDTO setInstrumentDailylifeKpi(List<AnRadioDTO> anRadioDTOS){
        //for compute
        SecondCategoryKpiDTO secondCategoryKpiDTO = new SecondCategoryKpiDTO();
        //total score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();
        totalScore = totalScore/100;

        double finalScore = 0;

        if(totalScore == 9){
            finalScore = 10 ;
        }else if(totalScore >= 8){
            finalScore = 8;
        }else if(totalScore >= 6){
            finalScore = 6;
        }else if(totalScore >= 4){
            finalScore = 4;
        }else if(totalScore >= 1){
            finalScore = 2;
        }else if(totalScore >= 0){
            finalScore = 0;
        }else{
            //todo
        }

        secondCategoryKpiDTO.setScore(finalScore);
        return secondCategoryKpiDTO;
    }
    /**
     * Second category personalization function :
     * Perceptive ability
     */
    private SecondCategoryKpiDTO setPerceptiveAbilityKpi(List<AnRadioDTO> anRadioDTOS){
        //for compute
        SecondCategoryKpiDTO secondCategoryKpiDTO = new SecondCategoryKpiDTO();
        //total score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();
        totalScore = totalScore/100;

        double finalScore = 0;
        if(totalScore == 31){
            finalScore = 10 ;
        }else if(totalScore > 24){
            finalScore = 8;
        }else if(totalScore > 17){
            finalScore = 6;
        }else if(totalScore > 9){
            finalScore = 4;
        }else if(totalScore >= 1){
            finalScore = 2;
        }else if(totalScore >= 0){
            finalScore = 0;
        }else{
            //todo
        }
        secondCategoryKpiDTO.setScore(finalScore);
        return secondCategoryKpiDTO;
    }
    /**
     * Second category personalization function :
     * Social participation ability
     */
    private SecondCategoryKpiDTO setSocialParticipationKpi(List<AnRadioDTO> anRadioDTOS){
        //for compute
        SecondCategoryKpiDTO secondCategoryKpiDTO = new SecondCategoryKpiDTO();
        //total score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();
        totalScore = totalScore/100;

        double finalScore = 0;
        if(totalScore > 6){
            finalScore = 2 ;
        }else if(totalScore > 2){
            finalScore = 1;
        }else if(totalScore > 0){
            finalScore = 0;
        }else{
            //todo
        }
        secondCategoryKpiDTO.setScore(finalScore);
        return secondCategoryKpiDTO;
    }
    /**
     * Second category personalization function :
     * The pain of quantitative
     */
    private SecondCategoryKpiDTO setPainOfQuantitativeKpi(List<AnRadioDTO> anRadioDTOS){
        //for compute
        SecondCategoryKpiDTO secondCategoryKpiDTO = new SecondCategoryKpiDTO();
        //total score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();

        double finalScore = 0;
        if(totalScore == 16){
            finalScore = 5;
        }else if(totalScore > 60){
            finalScore = 0 ;
        }else if(totalScore > 50){
            finalScore = 1;
        }else if(totalScore > 40){
            finalScore = 2;
        }else if(totalScore > 30){
            finalScore = 3;
        }else if(totalScore > 16){
            finalScore = 4;
        }else{
            //todo
        }
        secondCategoryKpiDTO.setScore(finalScore);
        return secondCategoryKpiDTO;
    }
    /**
     * Second category personalization function :
     * The initiative
     */
    private SecondCategoryKpiDTO setInitiativeKpi(List<AnRadioDTO> anRadioDTOS){
        //for compute
        SecondCategoryKpiDTO secondCategoryKpiDTO = new SecondCategoryKpiDTO();
        //total score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();
        double finalScore = totalScore/100;

        secondCategoryKpiDTO.setScore(finalScore);
        return secondCategoryKpiDTO;
    }

    /**
     * Second category personalization function :
     * balance ability
     */
    private SecondCategoryKpiDTO setBalanceKpi(List<AnRadioDTO> anRadioDTOS){
        //for compute
        SecondCategoryKpiDTO secondCategoryKpiDTO = new SecondCategoryKpiDTO();
        //total score
        double totalScore = anRadioDTOS.stream().mapToInt(AnRadioDTO::getOptionScore).sum();

        double finalScore = 0;
        if(totalScore == 9){
            finalScore = 3 ;
        }else if(totalScore > 5){
            finalScore = 2;
        }else if(totalScore > 2){
            finalScore = 1;
        }else if(totalScore >= 0) {
            finalScore = 0;
        }else{
            //todo
        }
        secondCategoryKpiDTO.setScore(finalScore);
        return secondCategoryKpiDTO;
    }


    /**
     * Draw a conclusion based on the score
     */
    private String getConclusionByTotalScore(Double total) {
        String conclu = "评测结论：";
        if (total != null) {
            if (total >= 85 && total <= 100) {
                conclu += "该被评测人为健康的老年人。";
            } else if (total >= 70) {
                conclu += "该被评测人为轻度失能的老年人。";
            } else if (55 <= total) {
                conclu += "该被评测人为中度失能的老年人。";
            } else if (0 <= total) {
                conclu += "该被评测人为重度失能的老年人。";
            } else {
                conclu += "该评测异常。";
            }
            return conclu;
        } else {
            return null;
        }
    }
    //set analyse status
    private String getStatusStr(List<String> statusStr) {
        if (statusStr.contains(AppraiseStutasEnum.SERIOUS.getName())) {
            return AppraiseStutasEnum.SERIOUS.getName();
        } else if (statusStr.contains(AppraiseStutasEnum.WARN.getName())) {
            return AppraiseStutasEnum.WARN.getName();
        } else {
            return AppraiseStutasEnum.NORMAL.getName();
        }
    }

    private String getDescription(List<AnRadioDTO> markAnRadio, List<AnRadioDTO> induce, String desc) {
        StringBuilder marking = new StringBuilder();
        if (markAnRadio != null && markAnRadio.size() > 0) {
            marking.append("<span style=\"color:red\">【重点标注问题】：</span><br/>");
            for (AnRadioDTO anRadioDTO : markAnRadio) {
                marking.append("问题：" + anRadioDTO.getQuestionName() + "&nbsp选择的是：" + anRadioDTO.getOptionAnswer() + "<br/>");
            }
        }
        if (induce != null && induce.size() > 0) {
            marking.append("<span style=\"color:orange\">【应该注意问题】：</span><br/>");
            for (AnRadioDTO anRadioDTO : induce) {
                marking.append("问题：" + anRadioDTO.getQuestionName() + "&nbsp选择的是：" + anRadioDTO.getOptionAnswer() + " <br/>");
            }
        }
        return "<span style=\"color:green\">【评测结论】：</span><br/>" + desc + "<br/>" + marking.toString();
    }

}
